package com.dysoft.flesh.ui;

import com.dysoft.bones.DataManager;
import com.dysoft.flesh.*;

import javax.vecmath.Matrix4f;
import javax.vecmath.Vector3f;
import java.awt.*;
import java.awt.geom.Area;
import java.awt.geom.RoundRectangle2D;
import java.util.Stack;

/**
 * @author Sean Micklethwaite
 *         Oct 30, 2010 4:17:15 PM
 */
public interface UiRenderer {
	void render(Context ctx) throws DataManager.DataObject.NotPresentException;

	public interface Component {
		void activate(Context ctx);
		void deactivate(Context ctx);
	}

	public class Decorator implements UiRenderer {
		final Component component;
		final UiRenderer renderer;

		public Decorator(Component component, UiRenderer renderer) {
			this.component = component;
			this.renderer = renderer;
		}

		public void render(Context ctx) throws DataManager.DataObject.NotPresentException {
			component.activate(ctx);
			renderer.render(ctx);
			component.deactivate(ctx);
		}
	}

	public class RenderComponentDecorator implements UiRenderer {
		final RenderComponent component;
		final UiRenderer renderer;

		public RenderComponentDecorator(RenderComponent component, UiRenderer renderer) {
			this.component = component;
			this.renderer = renderer;
		}

		public void render(Context ctx) throws DataManager.DataObject.NotPresentException {
			component.activate(ctx.getRenderContext());
			renderer.render(ctx);
			component.deactivate(ctx.getRenderContext());
		}
	}

	public class RendererAdaptor implements UiRenderer {
		final Renderer renderer;

		public RendererAdaptor(Renderer renderer) {
			this.renderer = renderer;
		}

		public void render(Context ctx) throws DataManager.DataObject.NotPresentException {
			renderer.render(ctx.getRenderContext());
		}
	}

	public class FitRegion implements Component {
		public void activate(Context ctx) {
			Rectangle bounds = ctx.getRegion();

			ctx.pushTransform();
			ctx.translate(new Vector3f((float)bounds.getMinX(), (float)bounds.getMinY(), 0));
			ctx.scale(new float [] { (float)bounds.getWidth(), (float)bounds.getHeight(), 1 });
		}

		public void deactivate(Context ctx) {
			ctx.popTransform();
		}
	}

	public class WidgetRenderer implements UiRenderer {
		final UiWidget widget;

		public WidgetRenderer(UiWidget widget) {
			this.widget = widget;
		}

		public void render(Context ctx) throws DataManager.DataObject.NotPresentException {
			renderWidget(ctx, widget);
		}

		public static void renderWidget(Context ctx, UiWidget widget)
				throws DataManager.DataObject.NotPresentException {
			Shape shape = widget.getShape();
			Rectangle bounds = shape.getBounds();
			bounds.translate(ctx.getOffset().x, ctx.getOffset().y);
			if(ctx.getRegion().intersects(bounds)) {
				ctx.pushShape(shape);
				try {
					widget.render(ctx);
				} finally {
					ctx.popShape();
				}
			}
		}

		public UiWidget getWidget() {
			return widget;
		}
	}

	public abstract class Context implements RenderContext {
		final RenderContext ctx;
		final Stack<Rectangle> region = new Stack<Rectangle>();
		final Stack<Shape> shape = new Stack<Shape>();
		final Stack<Point> offset = new Stack<Point>();

		public Context(RenderContext ctx, Shape region, Point offset) {
			this.ctx = ctx;
			this.region.push(region.getBounds());
			this.shape.push(region);
			this.offset.push(offset);
		}

		public Rectangle getRegion() {
			return region.peek();
		}

		public Shape getShape() {
			return shape.peek();
		}

		public void pushShape(Shape shape) {
			this.shape.push(shape);
			pushRegion(shape.getBounds());
		}

		public Shape popShape() {
			popRegion();
			return this.shape.pop();
		}

		/**
		 * Sets the active draw region
		 */
		public Rectangle pushRegion(Rectangle region) {
			region.translate(getOffset().x, getOffset().y);
			Rectangle r = this.region.push(
					this.region.isEmpty() ? region : region.intersection(getRegion())
			);
			setDrawRegion(r);
			return r;
		}

		public Rectangle popRegion() {
			Rectangle r = region.pop();
			setDrawRegion(getRegion());
			return r;
		}

		public Point pushOffset(Point point) {
			return offset.push(point);
		}

		public Point popOffset() {
			return offset.pop();
		}

		public Point getOffset() {
			return offset.peek();
		}

		public RenderTarget getTarget() {
			return ctx.getTarget();
		}

		public void pushTarget(RenderTarget target) {
			ctx.pushTarget(target);
		}

		public void popTarget() {
			ctx.popTarget();
		}

		public Renderer.Root getRenderer() {
			return ctx.getRenderer();
		}

		public void pushTransform() {
			ctx.pushTransform();
		}

		public void popTransform() {
			ctx.popTransform();
		}

		public void translate(int[] v) {
			ctx.translate(v);
		}

		public void translate(Vector3f v) {
			ctx.translate(v);
		}

		public void transform(Matrix4f m) {
			ctx.transform(m);
		}

		public void scale(float[] v) {
			ctx.scale(v);
		}

		public void drawRect(int[] topLeft, int[] topRight) {
			ctx.drawRect(topLeft, topRight);
		}

		public void pushRenderer(Renderer renderer) {
			ctx.pushRenderer(renderer);
		}

		public void popRenderer() {
			ctx.popRenderer();
		}

		public boolean isRendering() {
			return ctx.isRendering();
		}

		public int getTransformDepth() {
			return ctx.getTransformDepth();
		}

		public RenderContext getRenderContext() {
			return ctx;
		}

		abstract protected void setDrawRegion(Rectangle region);
	}

	class RoundedRectangle implements Component {
		int rx, ry;

		public RoundedRectangle(int rx, int ry) {
			this.rx = rx;
			this.ry = ry;
		}

		public void activate(Context ctx) {
			Rectangle bounds = ctx.getShape().getBounds();
			ctx.pushShape(new RoundRectangle2D.Double(
					bounds.getX(), bounds.getY(), bounds.getWidth(),
					bounds.getHeight(), rx, ry
			));
		}

		public void deactivate(Context ctx) {
			ctx.popShape();
		}
	}
}
